package org.smart4j.framework;

import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.bean.Data;
import org.smart4j.framework.bean.Handler;
import org.smart4j.framework.bean.Param;
import org.smart4j.framework.bean.View;
import org.smart4j.framework.helper.BeanHelper;
import org.smart4j.framework.helper.ConfigHelper;
import org.smart4j.framework.helper.ControllerHelper;
import org.smart4j.framework.helper.HelperLoader;
import org.smart4j.framework.utils.CodecUtil;
import org.smart4j.framework.utils.JsonUntil;
import org.smart4j.framework.utils.ReflectionUtil;
import org.smart4j.framework.utils.StreamUtil;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Wenxin on 2017/2/26.
 */
public class DispatchServlet extends HttpServlet {

    private static final Logger LOGGER = LoggerFactory.getLogger(DispatchServlet.class);

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//      根据请求方法和请求路径获取处理方法
        String requestMethod = req.getMethod().toLowerCase();
        String requestPath = req.getPathInfo();
        Handler handler = ControllerHelper.getHandler(requestMethod, requestPath);
        if (handler == null) {
//           无法处理此请求
            if (LOGGER.isDebugEnabled()) {
//                请求转发给容器默认servlet
                LOGGER.debug("No mapping for request path: {} in dispatch servlet", requestPath);
                LOGGER.debug("handle request using default servlet!");
                RequestDispatcher dispatcher = req.getServletContext().getNamedDispatcher("default");
                dispatcher.forward(req,resp);
            }
        } else {
//            获取请求参数
            Enumeration<String> parameterNames = req.getParameterNames();
            Map<String, Object> parameMap = new HashMap<String, Object>();
//            获取url参数
            while (parameterNames.hasMoreElements()) {
                String paramName = parameterNames.nextElement();
                String paramValue = req.getParameter(paramName);
                parameMap.put(paramName, paramValue);
            }

            //          获取处理此请求的Controller
            Method actionMethod = handler.getActionMethod();
            Class<?> controllerClass = handler.getControllerClass();
            Object bean = BeanHelper.getBean(controllerClass);

            Parameter[] parameters = actionMethod.getParameters();
            Object[] params = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                Class<?> type = parameters[i].getType();
                if (type.isAssignableFrom(ServletRequest.class) || type.equals(HttpServletRequest.class)) {
                    params[i] = req;
                }
                if (type.isAssignableFrom(ServletResponse.class) || type.equals(HttpServletResponse.class)) {
                    params[i] = resp;
                }

                if (type.isAssignableFrom(HttpSession.class)) {
                    params[i] = req.getSession(true);
                }
                if (type.isAssignableFrom(Param.class)) {
                    Param param = new Param(parameMap);
//            获取request body参数
                    String requestBody = CodecUtil.decodeURL(StreamUtil.getString(req.getInputStream()));
                    if (org.apache.commons.lang.StringUtils.isNotEmpty(requestBody)) {
                        String[] paramsStr = requestBody.split("&");
                        if (ArrayUtils.isNotEmpty(params)) {
                            for (String paramStr : paramsStr) {
                                String[] paramNV = paramStr.split("=");
                                if (ArrayUtils.isNotEmpty(paramNV) && paramNV.length == 2) {
                                    String paramName = paramNV[0];
                                    String paramValue = paramNV[1];
                                    parameMap.put(paramStr, paramValue);
                                }
                            }
                        }
                    }
                    params[i] = param;
                }

            }


//            反射执行Controller
            Object result = ReflectionUtil.invokedMethod(bean, actionMethod, params);
            if (result != null) {
                //           视图对象，请求转发，渲染视图
                if (result instanceof View) {
                    View view = (View) result;
                    String path = view.getPath();
                    if (org.apache.commons.lang.StringUtils.isNotEmpty(path)) {
                        if (path.startsWith("/")) {
                            resp.sendRedirect(path);
                        } else {
                            Map<String, Object> model = view.getModel();
                            for (Map.Entry<String, Object> entry : model.entrySet()) {
                                req.setAttribute(entry.getKey(), entry.getValue());
                            }
                            req.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(req, resp);
                        }
                    }
                } else if (result instanceof Data) {
//            Data对象，返回json
                    Data data = (Data) result;
                    Object model = data.getModel();
                    if (model != null) {
                        resp.setContentType("application/json");
                        resp.setCharacterEncoding("utf-8");
                        PrintWriter writer = resp.getWriter();

                        String json = JsonUntil.toJson(model);
                        writer.write(json);
                        writer.flush();
                        writer.close();
                    }
                } else
                    LOGGER.debug("can not handle the type of result: {}", result.getClass().getName());
            }
        }

    }

    @Override
    public void init(ServletConfig config) throws ServletException {

        HelperLoader.init();

        ServletContext servletContext = config.getServletContext();
        ServletRegistration jsp = servletContext.getServletRegistration("jsp");
        jsp.addMapping(ConfigHelper.getAppJspPath() + "*");

        ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
        defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
    }
}
